package com.ylsk.framework.config.ext;

import com.google.common.base.Strings;
import com.ylsk.common.annotation.Id2Name;
import com.ylsk.common.enums.Id2NameType;
import com.ylsk.common.utils.ClassUtils;
import com.ylsk.common.utils.MapCacheContainer;
import org.apache.ibatis.annotations.AutomapConstructor;
import org.apache.ibatis.binding.MapperMethod;
import org.apache.ibatis.cache.CacheKey;
import org.apache.ibatis.cursor.Cursor;
import org.apache.ibatis.executor.ErrorContext;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.executor.ExecutorException;
import org.apache.ibatis.executor.loader.ResultLoader;
import org.apache.ibatis.executor.loader.ResultLoaderMap;
import org.apache.ibatis.executor.parameter.ParameterHandler;
import org.apache.ibatis.executor.result.DefaultResultContext;
import org.apache.ibatis.executor.result.DefaultResultHandler;
import org.apache.ibatis.executor.result.ResultMapException;
import org.apache.ibatis.executor.resultset.ResultSetHandler;
import org.apache.ibatis.executor.resultset.ResultSetWrapper;
import org.apache.ibatis.mapping.*;
import org.apache.ibatis.reflection.MetaClass;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.ReflectorFactory;
import org.apache.ibatis.reflection.factory.ObjectFactory;
import org.apache.ibatis.session.*;
import org.apache.ibatis.type.JdbcType;
import org.apache.ibatis.type.TypeHandler;
import org.apache.ibatis.type.TypeHandlerRegistry;
import org.apache.ibatis.util.MapUtil;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.sql.CallableStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.*;

public class CustomDefaultResultSetHandler implements ResultSetHandler {
    private static final Object DEFERRED = new Object();
    private final Executor executor;
    private final Configuration configuration;
    private final MappedStatement mappedStatement;
    private final RowBounds rowBounds;
    private final ParameterHandler parameterHandler;
    private final ResultHandler<?> resultHandler;
    private final BoundSql boundSql;
    private final TypeHandlerRegistry typeHandlerRegistry;
    private final ObjectFactory objectFactory;
    private final ReflectorFactory reflectorFactory;
    private final Map<CacheKey, Object> nestedResultObjects = new HashMap();
    private final Map<String, Object> ancestorObjects = new HashMap();
    private Object previousRowValue;
    private final Map<String, ResultMapping> nextResultMaps = new HashMap();
    private final Map<CacheKey, List<CustomDefaultResultSetHandler.PendingRelation>> pendingRelations = new HashMap();
    private final Map<String, List<CustomDefaultResultSetHandler.UnMappedColumnAutoMapping>> autoMappingsCache = new HashMap();
    private boolean useConstructorMappings;

    public CustomDefaultResultSetHandler(Executor executor, MappedStatement mappedStatement, ParameterHandler parameterHandler, ResultHandler<?> resultHandler, BoundSql boundSql, RowBounds rowBounds) {
        this.executor = executor;
        this.configuration = mappedStatement.getConfiguration();
        this.mappedStatement = mappedStatement;
        this.rowBounds = rowBounds;
        this.parameterHandler = parameterHandler;
        this.boundSql = boundSql;
        this.typeHandlerRegistry = this.configuration.getTypeHandlerRegistry();
        this.objectFactory = this.configuration.getObjectFactory();
        this.reflectorFactory = this.configuration.getReflectorFactory();
        this.resultHandler = resultHandler;
    }

    public void handleOutputParameters(CallableStatement cs) throws SQLException {
        Object parameterObject = this.parameterHandler.getParameterObject();
        MetaObject metaParam = this.configuration.newMetaObject(parameterObject);
        List<ParameterMapping> parameterMappings = this.boundSql.getParameterMappings();

        for(int i = 0; i < parameterMappings.size(); ++i) {
            ParameterMapping parameterMapping = (ParameterMapping)parameterMappings.get(i);
            if (parameterMapping.getMode() == ParameterMode.OUT || parameterMapping.getMode() == ParameterMode.INOUT) {
                if (ResultSet.class.equals(parameterMapping.getJavaType())) {
                    this.handleRefCursorOutputParameter((ResultSet)cs.getObject(i + 1), parameterMapping, metaParam);
                } else {
                    TypeHandler<?> typeHandler = parameterMapping.getTypeHandler();
                    metaParam.setValue(parameterMapping.getProperty(), typeHandler.getResult(cs, i + 1));
                }
            }
        }

    }

    private void handleRefCursorOutputParameter(ResultSet rs, ParameterMapping parameterMapping, MetaObject metaParam) throws SQLException {
        if (rs != null) {
            try {
                String resultMapId = parameterMapping.getResultMapId();
                ResultMap resultMap = this.configuration.getResultMap(resultMapId);
                ResultSetWrapper rsw = new ResultSetWrapper(rs, this.configuration);
                if (this.resultHandler == null) {
                    DefaultResultHandler resultHandler = new DefaultResultHandler(this.objectFactory);
                    this.handleRowValues(rsw, resultMap, resultHandler, new RowBounds(), (ResultMapping)null);
                    metaParam.setValue(parameterMapping.getProperty(), resultHandler.getResultList());
                } else {
                    this.handleRowValues(rsw, resultMap, this.resultHandler, new RowBounds(), (ResultMapping)null);
                }
            } finally {
                this.closeResultSet(rs);
            }

        }
    }

    public List<Object> handleResultSets(Statement stmt) throws SQLException {
        ErrorContext.instance().activity("handling results").object(this.mappedStatement.getId());
        List<Object> multipleResults = new ArrayList();
        int resultSetCount = 0;
        ResultSetWrapper rsw = this.getFirstResultSet(stmt);
        List<ResultMap> resultMaps = this.mappedStatement.getResultMaps();
        int resultMapCount = resultMaps.size();
        this.validateResultMapsCount(rsw, resultMapCount);

        while(rsw != null && resultMapCount > resultSetCount) {
            ResultMap resultMap = (ResultMap)resultMaps.get(resultSetCount);
            this.handleResultSet(rsw, resultMap, multipleResults, (ResultMapping)null);
            rsw = this.getNextResultSet(stmt);
            this.cleanUpAfterHandlingResultSet();
            ++resultSetCount;
        }

        String[] resultSets = this.mappedStatement.getResultSets();
        if (resultSets != null) {
            while(rsw != null && resultSetCount < resultSets.length) {
                ResultMapping parentMapping = (ResultMapping)this.nextResultMaps.get(resultSets[resultSetCount]);
                if (parentMapping != null) {
                    String nestedResultMapId = parentMapping.getNestedResultMapId();
                    ResultMap resultMap = this.configuration.getResultMap(nestedResultMapId);
                    this.handleResultSet(rsw, resultMap, (List)null, parentMapping);
                }

                rsw = this.getNextResultSet(stmt);
                this.cleanUpAfterHandlingResultSet();
                ++resultSetCount;
            }
        }

        return this.collapseSingleResultList(multipleResults);
    }

    public <E> Cursor<E> handleCursorResultSets(Statement stmt) throws SQLException {
        ErrorContext.instance().activity("handling cursor results").object(this.mappedStatement.getId());
        ResultSetWrapper rsw = this.getFirstResultSet(stmt);
        List<ResultMap> resultMaps = this.mappedStatement.getResultMaps();
        int resultMapCount = resultMaps.size();
        this.validateResultMapsCount(rsw, resultMapCount);
        if (resultMapCount != 1) {
            throw new ExecutorException("Cursor results cannot be mapped to multiple resultMaps");
        } else {
            ResultMap resultMap = (ResultMap)resultMaps.get(0);
            return new CustomDefaultCursor<E>(this, resultMap, rsw, this.rowBounds);
        }
    }

    private ResultSetWrapper getFirstResultSet(Statement stmt) throws SQLException {
        ResultSet rs = stmt.getResultSet();

        while(rs == null) {
            if (stmt.getMoreResults()) {
                rs = stmt.getResultSet();
            } else if (stmt.getUpdateCount() == -1) {
                break;
            }
        }

        return rs != null ? new ResultSetWrapper(rs, this.configuration) : null;
    }

    private ResultSetWrapper getNextResultSet(Statement stmt) {
        try {
            if (stmt.getConnection().getMetaData().supportsMultipleResultSets() && (stmt.getMoreResults() || stmt.getUpdateCount() != -1)) {
                ResultSet rs = stmt.getResultSet();
                if (rs == null) {
                    return this.getNextResultSet(stmt);
                }

                return new ResultSetWrapper(rs, this.configuration);
            }
        } catch (Exception var3) {
        }

        return null;
    }

    private void closeResultSet(ResultSet rs) {
        try {
            if (rs != null) {
                rs.close();
            }
        } catch (SQLException var3) {
        }

    }

    private void cleanUpAfterHandlingResultSet() {
        this.nestedResultObjects.clear();
    }

    private void validateResultMapsCount(ResultSetWrapper rsw, int resultMapCount) {
        if (rsw != null && resultMapCount < 1) {
            throw new ExecutorException("A query was run and no Result Maps were found for the Mapped Statement '" + this.mappedStatement.getId() + "'.  It's likely that neither a Result Type nor a Result Map was specified.");
        }
    }

    private void handleResultSet(ResultSetWrapper rsw, ResultMap resultMap, List<Object> multipleResults, ResultMapping parentMapping) throws SQLException {
        try {
            if (parentMapping != null) {
                this.handleRowValues(rsw, resultMap, (ResultHandler)null, RowBounds.DEFAULT, parentMapping);
            } else if (this.resultHandler == null) {
                DefaultResultHandler defaultResultHandler = new DefaultResultHandler(this.objectFactory);
                this.handleRowValues(rsw, resultMap, defaultResultHandler, this.rowBounds, (ResultMapping)null);
                multipleResults.add(defaultResultHandler.getResultList());
            } else {
                this.handleRowValues(rsw, resultMap, this.resultHandler, this.rowBounds, (ResultMapping)null);
            }
        } finally {
            this.closeResultSet(rsw.getResultSet());
        }

    }

    private List<Object> collapseSingleResultList(List<Object> multipleResults) {
        return multipleResults.size() == 1 ? (List)multipleResults.get(0) : multipleResults;
    }

    public void handleRowValues(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler<?> resultHandler, RowBounds rowBounds, ResultMapping parentMapping) throws SQLException {
        if (resultMap.hasNestedResultMaps()) {
            this.ensureNoRowBounds();
            this.checkResultHandler();
            this.handleRowValuesForNestedResultMap(rsw, resultMap, resultHandler, rowBounds, parentMapping);
        } else {
            this.handleRowValuesForSimpleResultMap(rsw, resultMap, resultHandler, rowBounds, parentMapping);
        }

    }

    private void ensureNoRowBounds() {
        if (this.configuration.isSafeRowBoundsEnabled() && this.rowBounds != null && (this.rowBounds.getLimit() < 2147483647 || this.rowBounds.getOffset() > 0)) {
            throw new ExecutorException("Mapped Statements with nested result mappings cannot be safely constrained by RowBounds. Use safeRowBoundsEnabled=false setting to bypass this check.");
        }
    }

    protected void checkResultHandler() {
        if (this.resultHandler != null && this.configuration.isSafeResultHandlerEnabled() && !this.mappedStatement.isResultOrdered()) {
            throw new ExecutorException("Mapped Statements with nested result mappings cannot be safely used with a custom ResultHandler. Use safeResultHandlerEnabled=false setting to bypass this check or ensure your statement returns ordered data and set resultOrdered=true on it.");
        }
    }

    private void handleRowValuesForSimpleResultMap(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler<?> resultHandler, RowBounds rowBounds, ResultMapping parentMapping) throws SQLException {
        DefaultResultContext<Object> resultContext = new DefaultResultContext();
        ResultSet resultSet = rsw.getResultSet();
        this.skipRows(resultSet, rowBounds);

        while(this.shouldProcessMoreRows(resultContext, rowBounds) && !resultSet.isClosed() && resultSet.next()) {
            ResultMap discriminatedResultMap = this.resolveDiscriminatedResultMap(resultSet, resultMap, (String)null);
            Object rowValue = this.getRowValue(rsw, discriminatedResultMap, (String)null);
            this.storeObject(resultHandler, resultContext, rowValue, parentMapping, resultSet);
        }

    }

    private void storeObject(ResultHandler<?> resultHandler, DefaultResultContext<Object> resultContext, Object rowValue, ResultMapping parentMapping, ResultSet rs) throws SQLException {
        if (parentMapping != null) {
            this.linkToParents(rs, parentMapping, rowValue);
        } else {
            this.callResultHandler(resultHandler, resultContext, rowValue);
        }

    }

    private void callResultHandler(ResultHandler<?> resultHandler, DefaultResultContext<Object> resultContext, Object rowValue) {
        resultContext.nextResultObject(rowValue);
        ((ResultHandler<Object>) resultHandler).handleResult(resultContext);
    }

    private boolean shouldProcessMoreRows(ResultContext<?> context, RowBounds rowBounds) {
        return !context.isStopped() && context.getResultCount() < rowBounds.getLimit();
    }

    private void skipRows(ResultSet rs, RowBounds rowBounds) throws SQLException {
        if (rs.getType() != 1003) {
            if (rowBounds.getOffset() != 0) {
                rs.absolute(rowBounds.getOffset());
            }
        } else {
            for(int i = 0; i < rowBounds.getOffset() && rs.next(); ++i) {
            }
        }

    }

    private Object getRowValue(ResultSetWrapper rsw, ResultMap resultMap, String columnPrefix) throws SQLException {
        ResultLoaderMap lazyLoader = new ResultLoaderMap();
        Object rowValue = this.createResultObject(rsw, resultMap, lazyLoader, columnPrefix);
        if (rowValue != null && !this.hasTypeHandlerForResultObject(rsw, resultMap.getType())) {
            MetaObject metaObject = this.configuration.newMetaObject(rowValue);
            boolean foundValues = this.useConstructorMappings;
            if (this.shouldApplyAutomaticMappings(resultMap, false)) {
                foundValues = this.applyAutomaticMappings(rsw, resultMap, metaObject, columnPrefix) || foundValues;
            }

            foundValues = this.applyPropertyMappings(rsw, resultMap, metaObject, lazyLoader, columnPrefix) || foundValues;
            foundValues = lazyLoader.size() > 0 || foundValues;
            rowValue = !foundValues && !this.configuration.isReturnInstanceForEmptyRow() ? null : rowValue;
        }

        return rowValue;
    }

    private Object getRowValue(ResultSetWrapper rsw, ResultMap resultMap, CacheKey combinedKey, String columnPrefix, Object partialObject) throws SQLException {
        String resultMapId = resultMap.getId();
        Object rowValue = partialObject;
        if (partialObject != null) {
            MetaObject metaObject = this.configuration.newMetaObject(partialObject);
            this.putAncestor(partialObject, resultMapId);
            this.applyNestedResultMappings(rsw, resultMap, metaObject, columnPrefix, combinedKey, false);
            this.ancestorObjects.remove(resultMapId);
        } else {
            ResultLoaderMap lazyLoader = new ResultLoaderMap();
            rowValue = this.createResultObject(rsw, resultMap, lazyLoader, columnPrefix);
            if (rowValue != null && !this.hasTypeHandlerForResultObject(rsw, resultMap.getType())) {
                MetaObject metaObject = this.configuration.newMetaObject(rowValue);
                boolean foundValues = this.useConstructorMappings;
                if (this.shouldApplyAutomaticMappings(resultMap, true)) {
                    foundValues = this.applyAutomaticMappings(rsw, resultMap, metaObject, columnPrefix) || foundValues;
                }

                foundValues = this.applyPropertyMappings(rsw, resultMap, metaObject, lazyLoader, columnPrefix) || foundValues;
                this.putAncestor(rowValue, resultMapId);
                foundValues = this.applyNestedResultMappings(rsw, resultMap, metaObject, columnPrefix, combinedKey, true) || foundValues;
                this.ancestorObjects.remove(resultMapId);
                foundValues = lazyLoader.size() > 0 || foundValues;
                rowValue = !foundValues && !this.configuration.isReturnInstanceForEmptyRow() ? null : rowValue;
            }

            if (combinedKey != CacheKey.NULL_CACHE_KEY) {
                this.nestedResultObjects.put(combinedKey, rowValue);
            }
        }

        return rowValue;
    }

    private void putAncestor(Object resultObject, String resultMapId) {
        this.ancestorObjects.put(resultMapId, resultObject);
    }

    private boolean shouldApplyAutomaticMappings(ResultMap resultMap, boolean isNested) {
        if (resultMap.getAutoMapping() != null) {
            return resultMap.getAutoMapping();
        } else if (isNested) {
            return AutoMappingBehavior.FULL == this.configuration.getAutoMappingBehavior();
        } else {
            return AutoMappingBehavior.NONE != this.configuration.getAutoMappingBehavior();
        }
    }

    private boolean applyPropertyMappings(ResultSetWrapper rsw, ResultMap resultMap, MetaObject metaObject, ResultLoaderMap lazyLoader, String columnPrefix) throws SQLException {
        List<String> mappedColumnNames = rsw.getMappedColumnNames(resultMap, columnPrefix);
        boolean foundValues = false;
        List<ResultMapping> propertyMappings = resultMap.getPropertyResultMappings();
        Iterator var9 = propertyMappings.iterator();

        while(true) {
            while(true) {
                Object value;
                String property;
                do {
                    ResultMapping propertyMapping;
                    String column;
                    do {
                        if (!var9.hasNext()) {
                            return foundValues;
                        }

                        propertyMapping = (ResultMapping)var9.next();
                        column = this.prependPrefix(propertyMapping.getColumn(), columnPrefix);
                        if (propertyMapping.getNestedResultMapId() != null) {
                            column = null;
                        }
                    } while(!propertyMapping.isCompositeResult() && (column == null || !mappedColumnNames.contains(column.toUpperCase(Locale.ENGLISH))) && propertyMapping.getResultSet() == null);

                    value = this.getPropertyMappingValue(rsw.getResultSet(), metaObject, propertyMapping, lazyLoader, columnPrefix);
                    property = propertyMapping.getProperty();
                } while(property == null);

                if (value == DEFERRED) {
                    foundValues = true;
                } else {
                    if (value != null) {
                        foundValues = true;
                    }

                    if (value != null || this.configuration.isCallSettersOnNulls() && !metaObject.getSetterType(property).isPrimitive()) {
                        metaObject.setValue(property, value);
                    }
                }
            }
        }
    }

    private Object getPropertyMappingValue(ResultSet rs, MetaObject metaResultObject, ResultMapping propertyMapping, ResultLoaderMap lazyLoader, String columnPrefix) throws SQLException {
        if (propertyMapping.getNestedQueryId() != null) {
            return this.getNestedQueryMappingValue(rs, metaResultObject, propertyMapping, lazyLoader, columnPrefix);
        } else if (propertyMapping.getResultSet() != null) {
            this.addPendingChildRelation(rs, metaResultObject, propertyMapping);
            return DEFERRED;
        } else {
            TypeHandler<?> typeHandler = propertyMapping.getTypeHandler();
            String column = this.prependPrefix(propertyMapping.getColumn(), columnPrefix);
            return typeHandler.getResult(rs, column);
        }
    }

    private List<CustomDefaultResultSetHandler.UnMappedColumnAutoMapping> createAutomaticMappings(ResultSetWrapper rsw, ResultMap resultMap, MetaObject metaObject, String columnPrefix) throws SQLException {
        String mapKey = resultMap.getId() + ":" + columnPrefix;
        List<CustomDefaultResultSetHandler.UnMappedColumnAutoMapping> autoMapping = (List)this.autoMappingsCache.get(mapKey);
        if (autoMapping == null) {
            autoMapping = new ArrayList();
            List<String> unmappedColumnNames = rsw.getUnmappedColumnNames(resultMap, columnPrefix);
            Iterator var8 = unmappedColumnNames.iterator();

            while(true) {
                while(true) {
                    String columnName;
                    String propertyName;
                    while(true) {
                        if (!var8.hasNext()) {
                            this.autoMappingsCache.put(mapKey, autoMapping);
                            return (List)autoMapping;
                        }

                        columnName = (String)var8.next();
                        propertyName = columnName;
                        if (columnPrefix == null || columnPrefix.isEmpty()) {
                            break;
                        }

                        if (columnName.toUpperCase(Locale.ENGLISH).startsWith(columnPrefix)) {
                            propertyName = columnName.substring(columnPrefix.length());
                            break;
                        }
                    }

                    String property = metaObject.findProperty(propertyName, this.configuration.isMapUnderscoreToCamelCase());
                    if (property != null && metaObject.hasSetter(property)) {
                        if (!resultMap.getMappedProperties().contains(property)) {
                            Class<?> propertyType = metaObject.getSetterType(property);
                            if (this.typeHandlerRegistry.hasTypeHandler(propertyType, rsw.getJdbcType(columnName))) {
                                TypeHandler<?> typeHandler = rsw.getTypeHandler(propertyType, columnName);
                                ((List)autoMapping).add(new CustomDefaultResultSetHandler.UnMappedColumnAutoMapping(columnName, property, typeHandler, propertyType.isPrimitive()));
                            } else {
                                this.configuration.getAutoMappingUnknownColumnBehavior().doAction(this.mappedStatement, columnName, property, propertyType);
                            }
                        }
                    } else {
                        this.configuration.getAutoMappingUnknownColumnBehavior().doAction(this.mappedStatement, columnName, property != null ? property : propertyName, (Class)null);
                    }
                }
            }
        } else {
            return (List)autoMapping;
        }
    }

    private boolean applyAutomaticMappings(ResultSetWrapper rsw, ResultMap resultMap, MetaObject metaObject, String columnPrefix) throws SQLException {
        List<CustomDefaultResultSetHandler.UnMappedColumnAutoMapping> autoMapping = this.createAutomaticMappings(rsw, resultMap, metaObject, columnPrefix);
        boolean foundValues = false;
        if (!autoMapping.isEmpty()) {
            Iterator var7 = autoMapping.iterator();

            while(true) {
                CustomDefaultResultSetHandler.UnMappedColumnAutoMapping mapping;
                Object value;
                do {
                    if (!var7.hasNext()) {
                        return foundValues;
                    }

                    mapping = (CustomDefaultResultSetHandler.UnMappedColumnAutoMapping)var7.next();
                    value = mapping.typeHandler.getResult(rsw.getResultSet(), mapping.column);
                    if (value != null) {
                        foundValues = true;
                    }
                } while(value == null && (!this.configuration.isCallSettersOnNulls() || mapping.primitive));
                handlerId2NMame(resultMap, metaObject, mapping.property, value);
                metaObject.setValue(mapping.property, value);
            }
        } else {
            return foundValues;
        }
    }

        private void handlerId2NMame(ResultMap resultMap, MetaObject metaObject, String idProperty, Object value) {
        if (resultMap.getType().getName().startsWith("com.ylsk.check.domain")) {
            try {
                Map<String, Field> fields = ClassUtils.listAllFields(resultMap.getType());
                Field field = fields.get(idProperty);
                Id2Name id2Name = field.getAnnotation(Id2Name.class);
                if (id2Name != null) {
                    String propertyName = idProperty;
                    propertyName = propertyName.replaceAll("Id", "") + "Name";
                    String nameProperty = metaObject.findProperty(propertyName, true);
                    if (Strings.isNullOrEmpty(nameProperty)) {
                        throw new RuntimeException("没有" + propertyName + "相关的Name属性");
                    }
                    String property = metaObject.findProperty(nameProperty, true);
                    Id2NameType id2NameType = id2Name.value();
                    String id2NameValue = MapCacheContainer.getInstance().id2Name2(id2NameType, value.toString());
                    metaObject.setValue(property, id2NameValue);
                }
            } finally {
                ClassUtils.remove();
            }
        }
    }

    private void linkToParents(ResultSet rs, ResultMapping parentMapping, Object rowValue) throws SQLException {
        CacheKey parentKey = this.createKeyForMultipleResults(rs, parentMapping, parentMapping.getColumn(), parentMapping.getForeignColumn());
        List<CustomDefaultResultSetHandler.PendingRelation> parents = (List)this.pendingRelations.get(parentKey);
        if (parents != null) {
            Iterator var6 = parents.iterator();

            while(var6.hasNext()) {
                CustomDefaultResultSetHandler.PendingRelation parent = (CustomDefaultResultSetHandler.PendingRelation)var6.next();
                if (parent != null && rowValue != null) {
                    this.linkObjects(parent.metaObject, parent.propertyMapping, rowValue);
                }
            }
        }

    }

    private void addPendingChildRelation(ResultSet rs, MetaObject metaResultObject, ResultMapping parentMapping) throws SQLException {
        CacheKey cacheKey = this.createKeyForMultipleResults(rs, parentMapping, parentMapping.getColumn(), parentMapping.getColumn());
        CustomDefaultResultSetHandler.PendingRelation deferLoad = new CustomDefaultResultSetHandler.PendingRelation();
        deferLoad.metaObject = metaResultObject;
        deferLoad.propertyMapping = parentMapping;
        List<CustomDefaultResultSetHandler.PendingRelation> relations = (List) MapUtil.computeIfAbsent(this.pendingRelations, cacheKey, (k) -> {
            return new ArrayList();
        });
        relations.add(deferLoad);
        ResultMapping previous = (ResultMapping)this.nextResultMaps.get(parentMapping.getResultSet());
        if (previous == null) {
            this.nextResultMaps.put(parentMapping.getResultSet(), parentMapping);
        } else if (!previous.equals(parentMapping)) {
            throw new ExecutorException("Two different properties are mapped to the same resultSet");
        }

    }

    private CacheKey createKeyForMultipleResults(ResultSet rs, ResultMapping resultMapping, String names, String columns) throws SQLException {
        CacheKey cacheKey = new CacheKey();
        cacheKey.update(resultMapping);
        if (columns != null && names != null) {
            String[] columnsArray = columns.split(",");
            String[] namesArray = names.split(",");

            for(int i = 0; i < columnsArray.length; ++i) {
                Object value = rs.getString(columnsArray[i]);
                if (value != null) {
                    cacheKey.update(namesArray[i]);
                    cacheKey.update(value);
                }
            }
        }

        return cacheKey;
    }

    private Object createResultObject(ResultSetWrapper rsw, ResultMap resultMap, ResultLoaderMap lazyLoader, String columnPrefix) throws SQLException {
        this.useConstructorMappings = false;
        List<Class<?>> constructorArgTypes = new ArrayList();
        List<Object> constructorArgs = new ArrayList();
        Object resultObject = this.createResultObject(rsw, resultMap, constructorArgTypes, constructorArgs, columnPrefix);
        if (resultObject != null && !this.hasTypeHandlerForResultObject(rsw, resultMap.getType())) {
            List<ResultMapping> propertyMappings = resultMap.getPropertyResultMappings();
            Iterator var9 = propertyMappings.iterator();

            while(var9.hasNext()) {
                ResultMapping propertyMapping = (ResultMapping)var9.next();
                if (propertyMapping.getNestedQueryId() != null && propertyMapping.isLazy()) {
                    resultObject = this.configuration.getProxyFactory().createProxy(resultObject, lazyLoader, this.configuration, this.objectFactory, constructorArgTypes, constructorArgs);
                    break;
                }
            }
        }

        this.useConstructorMappings = resultObject != null && !constructorArgTypes.isEmpty();
        return resultObject;
    }

    private Object createResultObject(ResultSetWrapper rsw, ResultMap resultMap, List<Class<?>> constructorArgTypes, List<Object> constructorArgs, String columnPrefix) throws SQLException {
        Class<?> resultType = resultMap.getType();
        MetaClass metaType = MetaClass.forClass(resultType, this.reflectorFactory);
        List<ResultMapping> constructorMappings = resultMap.getConstructorResultMappings();
        if (this.hasTypeHandlerForResultObject(rsw, resultType)) {
            return this.createPrimitiveResultObject(rsw, resultMap, columnPrefix);
        } else if (!constructorMappings.isEmpty()) {
            return this.createParameterizedResultObject(rsw, resultType, constructorMappings, constructorArgTypes, constructorArgs, columnPrefix);
        } else if (!resultType.isInterface() && !metaType.hasDefaultConstructor()) {
            if (this.shouldApplyAutomaticMappings(resultMap, false)) {
                return this.createByConstructorSignature(rsw, resultType, constructorArgTypes, constructorArgs);
            } else {
                throw new ExecutorException("Do not know how to create an instance of " + resultType);
            }
        } else {
            return this.objectFactory.create(resultType);
        }
    }

    Object createParameterizedResultObject(ResultSetWrapper rsw, Class<?> resultType, List<ResultMapping> constructorMappings, List<Class<?>> constructorArgTypes, List<Object> constructorArgs, String columnPrefix) {
        boolean foundValues = false;

        Object value;
        for(Iterator var8 = constructorMappings.iterator(); var8.hasNext(); foundValues = value != null || foundValues) {
            ResultMapping constructorMapping = (ResultMapping)var8.next();
            Class<?> parameterType = constructorMapping.getJavaType();
            String column = constructorMapping.getColumn();

            try {
                if (constructorMapping.getNestedQueryId() != null) {
                    value = this.getNestedQueryConstructorValue(rsw.getResultSet(), constructorMapping, columnPrefix);
                } else if (constructorMapping.getNestedResultMapId() != null) {
                    ResultMap resultMap = this.configuration.getResultMap(constructorMapping.getNestedResultMapId());
                    value = this.getRowValue(rsw, resultMap, this.getColumnPrefix(columnPrefix, constructorMapping));
                } else {
                    TypeHandler<?> typeHandler = constructorMapping.getTypeHandler();
                    value = typeHandler.getResult(rsw.getResultSet(), this.prependPrefix(column, columnPrefix));
                }
            } catch (SQLException | ResultMapException var14) {
                throw new ExecutorException("Could not process result for mapping: " + constructorMapping, var14);
            }

            constructorArgTypes.add(parameterType);
            constructorArgs.add(value);
        }

        return foundValues ? this.objectFactory.create(resultType, constructorArgTypes, constructorArgs) : null;
    }

    private Object createByConstructorSignature(ResultSetWrapper rsw, Class<?> resultType, List<Class<?>> constructorArgTypes, List<Object> constructorArgs) throws SQLException {
        Constructor<?>[] constructors = resultType.getDeclaredConstructors();
        Constructor<?> defaultConstructor = this.findDefaultConstructor(constructors);
        if (defaultConstructor != null) {
            return this.createUsingConstructor(rsw, resultType, constructorArgTypes, constructorArgs, defaultConstructor);
        } else {
            Constructor[] var7 = constructors;
            int var8 = constructors.length;

            for(int var9 = 0; var9 < var8; ++var9) {
                Constructor<?> constructor = var7[var9];
                if (this.allowedConstructorUsingTypeHandlers(constructor, rsw.getJdbcTypes())) {
                    return this.createUsingConstructor(rsw, resultType, constructorArgTypes, constructorArgs, constructor);
                }
            }

            throw new ExecutorException("No constructor found in " + resultType.getName() + " matching " + rsw.getClassNames());
        }
    }

    private Object createUsingConstructor(ResultSetWrapper rsw, Class<?> resultType, List<Class<?>> constructorArgTypes, List<Object> constructorArgs, Constructor<?> constructor) throws SQLException {
        boolean foundValues = false;

        for(int i = 0; i < constructor.getParameterTypes().length; ++i) {
            Class<?> parameterType = constructor.getParameterTypes()[i];
            String columnName = (String)rsw.getColumnNames().get(i);
            TypeHandler<?> typeHandler = rsw.getTypeHandler(parameterType, columnName);
            Object value = typeHandler.getResult(rsw.getResultSet(), columnName);
            constructorArgTypes.add(parameterType);
            constructorArgs.add(value);
            foundValues = value != null || foundValues;
        }

        return foundValues ? this.objectFactory.create(resultType, constructorArgTypes, constructorArgs) : null;
    }

    private Constructor<?> findDefaultConstructor(Constructor<?>[] constructors) {
        if (constructors.length == 1) {
            return constructors[0];
        } else {
            Constructor[] var2 = constructors;
            int var3 = constructors.length;

            for(int var4 = 0; var4 < var3; ++var4) {
                Constructor<?> constructor = var2[var4];
                if (constructor.isAnnotationPresent(AutomapConstructor.class)) {
                    return constructor;
                }
            }

            return null;
        }
    }

    private boolean allowedConstructorUsingTypeHandlers(Constructor<?> constructor, List< JdbcType > jdbcTypes) {
        Class<?>[] parameterTypes = constructor.getParameterTypes();
        if (parameterTypes.length != jdbcTypes.size()) {
            return false;
        } else {
            for(int i = 0; i < parameterTypes.length; ++i) {
                if (!this.typeHandlerRegistry.hasTypeHandler(parameterTypes[i], (JdbcType)jdbcTypes.get(i))) {
                    return false;
                }
            }

            return true;
        }
    }

    private Object createPrimitiveResultObject(ResultSetWrapper rsw, ResultMap resultMap, String columnPrefix) throws SQLException {
        Class<?> resultType = resultMap.getType();
        String columnName;
        if (!resultMap.getResultMappings().isEmpty()) {
            List<ResultMapping> resultMappingList = resultMap.getResultMappings();
            ResultMapping mapping = (ResultMapping)resultMappingList.get(0);
            columnName = this.prependPrefix(mapping.getColumn(), columnPrefix);
        } else {
            columnName = (String)rsw.getColumnNames().get(0);
        }

        TypeHandler<?> typeHandler = rsw.getTypeHandler(resultType, columnName);
        return typeHandler.getResult(rsw.getResultSet(), columnName);
    }

    private Object getNestedQueryConstructorValue(ResultSet rs, ResultMapping constructorMapping, String columnPrefix) throws SQLException {
        String nestedQueryId = constructorMapping.getNestedQueryId();
        MappedStatement nestedQuery = this.configuration.getMappedStatement(nestedQueryId);
        Class<?> nestedQueryParameterType = nestedQuery.getParameterMap().getType();
        Object nestedQueryParameterObject = this.prepareParameterForNestedQuery(rs, constructorMapping, nestedQueryParameterType, columnPrefix);
        Object value = null;
        if (nestedQueryParameterObject != null) {
            BoundSql nestedBoundSql = nestedQuery.getBoundSql(nestedQueryParameterObject);
            CacheKey key = this.executor.createCacheKey(nestedQuery, nestedQueryParameterObject, RowBounds.DEFAULT, nestedBoundSql);
            Class<?> targetType = constructorMapping.getJavaType();
            ResultLoader resultLoader = new ResultLoader(this.configuration, this.executor, nestedQuery, nestedQueryParameterObject, targetType, key, nestedBoundSql);
            value = resultLoader.loadResult();
        }

        return value;
    }

    private Object getNestedQueryMappingValue(ResultSet rs, MetaObject metaResultObject, ResultMapping propertyMapping, ResultLoaderMap lazyLoader, String columnPrefix) throws SQLException {
        String nestedQueryId = propertyMapping.getNestedQueryId();
        String property = propertyMapping.getProperty();
        MappedStatement nestedQuery = this.configuration.getMappedStatement(nestedQueryId);
        Class<?> nestedQueryParameterType = nestedQuery.getParameterMap().getType();
        Object nestedQueryParameterObject = this.prepareParameterForNestedQuery(rs, propertyMapping, nestedQueryParameterType, columnPrefix);
        Object value = null;
        if (nestedQueryParameterObject != null) {
            BoundSql nestedBoundSql = nestedQuery.getBoundSql(nestedQueryParameterObject);
            CacheKey key = this.executor.createCacheKey(nestedQuery, nestedQueryParameterObject, RowBounds.DEFAULT, nestedBoundSql);
            Class<?> targetType = propertyMapping.getJavaType();
            if (this.executor.isCached(nestedQuery, key)) {
                this.executor.deferLoad(nestedQuery, metaResultObject, property, key, targetType);
                value = DEFERRED;
            } else {
                ResultLoader resultLoader = new ResultLoader(this.configuration, this.executor, nestedQuery, nestedQueryParameterObject, targetType, key, nestedBoundSql);
                if (propertyMapping.isLazy()) {
                    lazyLoader.addLoader(property, metaResultObject, resultLoader);
                    value = DEFERRED;
                } else {
                    value = resultLoader.loadResult();
                }
            }
        }

        return value;
    }

    private Object prepareParameterForNestedQuery(ResultSet rs, ResultMapping resultMapping, Class<?> parameterType, String columnPrefix) throws SQLException {
        return resultMapping.isCompositeResult() ? this.prepareCompositeKeyParameter(rs, resultMapping, parameterType, columnPrefix) : this.prepareSimpleKeyParameter(rs, resultMapping, parameterType, columnPrefix);
    }

    private Object prepareSimpleKeyParameter(ResultSet rs, ResultMapping resultMapping, Class<?> parameterType, String columnPrefix) throws SQLException {
        TypeHandler typeHandler;
        if (this.typeHandlerRegistry.hasTypeHandler(parameterType)) {
            typeHandler = this.typeHandlerRegistry.getTypeHandler(parameterType);
        } else {
            typeHandler = this.typeHandlerRegistry.getUnknownTypeHandler();
        }

        return typeHandler.getResult(rs, this.prependPrefix(resultMapping.getColumn(), columnPrefix));
    }

    private Object prepareCompositeKeyParameter(ResultSet rs, ResultMapping resultMapping, Class<?> parameterType, String columnPrefix) throws SQLException {
        Object parameterObject = this.instantiateParameterObject(parameterType);
        MetaObject metaObject = this.configuration.newMetaObject(parameterObject);
        boolean foundValues = false;
        Iterator var8 = resultMapping.getComposites().iterator();

        while(var8.hasNext()) {
            ResultMapping innerResultMapping = (ResultMapping)var8.next();
            Class<?> propType = metaObject.getSetterType(innerResultMapping.getProperty());
            TypeHandler<?> typeHandler = this.typeHandlerRegistry.getTypeHandler(propType);
            Object propValue = typeHandler.getResult(rs, this.prependPrefix(innerResultMapping.getColumn(), columnPrefix));
            if (propValue != null) {
                metaObject.setValue(innerResultMapping.getProperty(), propValue);
                foundValues = true;
            }
        }

        return foundValues ? parameterObject : null;
    }

    private Object instantiateParameterObject(Class<?> parameterType) {
        if (parameterType == null) {
            return new HashMap();
        } else {
            return MapperMethod.ParamMap.class.equals(parameterType) ? new HashMap() : this.objectFactory.create(parameterType);
        }
    }

    public ResultMap resolveDiscriminatedResultMap(ResultSet rs, ResultMap resultMap, String columnPrefix) throws SQLException {
        Set<String> pastDiscriminators = new HashSet();
        Discriminator discriminator = resultMap.getDiscriminator();

        while(discriminator != null) {
            Object value = this.getDiscriminatorValue(rs, discriminator, columnPrefix);
            String discriminatedMapId = discriminator.getMapIdFor(String.valueOf(value));
            if (!this.configuration.hasResultMap(discriminatedMapId)) {
                break;
            }

            resultMap = this.configuration.getResultMap(discriminatedMapId);
            Discriminator lastDiscriminator = discriminator;
            discriminator = resultMap.getDiscriminator();
            if (discriminator == lastDiscriminator || !pastDiscriminators.add(discriminatedMapId)) {
                break;
            }
        }

        return resultMap;
    }

    private Object getDiscriminatorValue(ResultSet rs, Discriminator discriminator, String columnPrefix) throws SQLException {
        ResultMapping resultMapping = discriminator.getResultMapping();
        TypeHandler<?> typeHandler = resultMapping.getTypeHandler();
        return typeHandler.getResult(rs, this.prependPrefix(resultMapping.getColumn(), columnPrefix));
    }

    private String prependPrefix(String columnName, String prefix) {
        return columnName != null && columnName.length() != 0 && prefix != null && prefix.length() != 0 ? prefix + columnName : columnName;
    }

    private void handleRowValuesForNestedResultMap(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler<?> resultHandler, RowBounds rowBounds, ResultMapping parentMapping) throws SQLException {
        DefaultResultContext<Object> resultContext = new DefaultResultContext();
        ResultSet resultSet = rsw.getResultSet();
        this.skipRows(resultSet, rowBounds);
        Object rowValue = this.previousRowValue;

        while(this.shouldProcessMoreRows(resultContext, rowBounds) && !resultSet.isClosed() && resultSet.next()) {
            ResultMap discriminatedResultMap = this.resolveDiscriminatedResultMap(resultSet, resultMap, (String)null);
            CacheKey rowKey = this.createRowKey(discriminatedResultMap, rsw, (String)null);
            Object partialObject = this.nestedResultObjects.get(rowKey);
            if (this.mappedStatement.isResultOrdered()) {
                if (partialObject == null && rowValue != null) {
                    this.nestedResultObjects.clear();
                    this.storeObject(resultHandler, resultContext, rowValue, parentMapping, resultSet);
                }

                rowValue = this.getRowValue(rsw, discriminatedResultMap, rowKey, (String)null, partialObject);
            } else {
                rowValue = this.getRowValue(rsw, discriminatedResultMap, rowKey, (String)null, partialObject);
                if (partialObject == null) {
                    this.storeObject(resultHandler, resultContext, rowValue, parentMapping, resultSet);
                }
            }
        }

        if (rowValue != null && this.mappedStatement.isResultOrdered() && this.shouldProcessMoreRows(resultContext, rowBounds)) {
            this.storeObject(resultHandler, resultContext, rowValue, parentMapping, resultSet);
            this.previousRowValue = null;
        } else if (rowValue != null) {
            this.previousRowValue = rowValue;
        }

    }

    private boolean applyNestedResultMappings(ResultSetWrapper rsw, ResultMap resultMap, MetaObject metaObject, String parentPrefix, CacheKey parentRowKey, boolean newObject) {
        boolean foundValues = false;
        Iterator var8 = resultMap.getPropertyResultMappings().iterator();

        while(true) {
            ResultMapping resultMapping;
            String nestedResultMapId;
            do {
                do {
                    if (!var8.hasNext()) {
                        return foundValues;
                    }

                    resultMapping = (ResultMapping)var8.next();
                    nestedResultMapId = resultMapping.getNestedResultMapId();
                } while(nestedResultMapId == null);
            } while(resultMapping.getResultSet() != null);

            try {
                String columnPrefix = this.getColumnPrefix(parentPrefix, resultMapping);
                ResultMap nestedResultMap = this.getNestedResultMap(rsw.getResultSet(), nestedResultMapId, columnPrefix);
                if (resultMapping.getColumnPrefix() == null) {
                    Object ancestorObject = this.ancestorObjects.get(nestedResultMapId);
                    if (ancestorObject != null) {
                        if (newObject) {
                            this.linkObjects(metaObject, resultMapping, ancestorObject);
                        }
                        continue;
                    }
                }

                CacheKey rowKey = this.createRowKey(nestedResultMap, rsw, columnPrefix);
                CacheKey combinedKey = this.combineKeys(rowKey, parentRowKey);
                Object rowValue = this.nestedResultObjects.get(combinedKey);
                boolean knownValue = rowValue != null;
                this.instantiateCollectionPropertyIfAppropriate(resultMapping, metaObject);
                if (this.anyNotNullColumnHasValue(resultMapping, columnPrefix, rsw)) {
                    rowValue = this.getRowValue(rsw, nestedResultMap, combinedKey, columnPrefix, rowValue);
                    if (rowValue != null && !knownValue) {
                        this.linkObjects(metaObject, resultMapping, rowValue);
                        foundValues = true;
                    }
                }
            } catch (SQLException var17) {
                throw new ExecutorException("Error getting nested result map values for '" + resultMapping.getProperty() + "'.  Cause: " + var17, var17);
            }
        }
    }

    private String getColumnPrefix(String parentPrefix, ResultMapping resultMapping) {
        StringBuilder columnPrefixBuilder = new StringBuilder();
        if (parentPrefix != null) {
            columnPrefixBuilder.append(parentPrefix);
        }

        if (resultMapping.getColumnPrefix() != null) {
            columnPrefixBuilder.append(resultMapping.getColumnPrefix());
        }

        return columnPrefixBuilder.length() == 0 ? null : columnPrefixBuilder.toString().toUpperCase(Locale.ENGLISH);
    }

    private boolean anyNotNullColumnHasValue(ResultMapping resultMapping, String columnPrefix, ResultSetWrapper rsw) throws SQLException {
        Set<String> notNullColumns = resultMapping.getNotNullColumns();
        if (notNullColumns != null && !notNullColumns.isEmpty()) {
            ResultSet rs = rsw.getResultSet();
            Iterator var9 = notNullColumns.iterator();

            do {
                if (!var9.hasNext()) {
                    return false;
                }

                String column = (String)var9.next();
                rs.getObject(this.prependPrefix(column, columnPrefix));
            } while(rs.wasNull());

            return true;
        } else if (columnPrefix != null) {
            Iterator var5 = rsw.getColumnNames().iterator();

            String columnName;
            do {
                if (!var5.hasNext()) {
                    return false;
                }

                columnName = (String)var5.next();
            } while(!columnName.toUpperCase(Locale.ENGLISH).startsWith(columnPrefix.toUpperCase(Locale.ENGLISH)));

            return true;
        } else {
            return true;
        }
    }

    private ResultMap getNestedResultMap(ResultSet rs, String nestedResultMapId, String columnPrefix) throws SQLException {
        ResultMap nestedResultMap = this.configuration.getResultMap(nestedResultMapId);
        return this.resolveDiscriminatedResultMap(rs, nestedResultMap, columnPrefix);
    }

    private CacheKey createRowKey(ResultMap resultMap, ResultSetWrapper rsw, String columnPrefix) throws SQLException {
        CacheKey cacheKey = new CacheKey();
        cacheKey.update(resultMap.getId());
        List<ResultMapping> resultMappings = this.getResultMappingsForRowKey(resultMap);
        if (resultMappings.isEmpty()) {
            if (Map.class.isAssignableFrom(resultMap.getType())) {
                this.createRowKeyForMap(rsw, cacheKey);
            } else {
                this.createRowKeyForUnmappedProperties(resultMap, rsw, cacheKey, columnPrefix);
            }
        } else {
            this.createRowKeyForMappedProperties(resultMap, rsw, cacheKey, resultMappings, columnPrefix);
        }

        return cacheKey.getUpdateCount() < 2 ? CacheKey.NULL_CACHE_KEY : cacheKey;
    }

    private CacheKey combineKeys(CacheKey rowKey, CacheKey parentRowKey) {
        if (rowKey.getUpdateCount() > 1 && parentRowKey.getUpdateCount() > 1) {
            CacheKey combinedKey;
            try {
                combinedKey = rowKey.clone();
            } catch (CloneNotSupportedException var5) {
                throw new ExecutorException("Error cloning cache key.  Cause: " + var5, var5);
            }

            combinedKey.update(parentRowKey);
            return combinedKey;
        } else {
            return CacheKey.NULL_CACHE_KEY;
        }
    }

    private List<ResultMapping> getResultMappingsForRowKey(ResultMap resultMap) {
        List<ResultMapping> resultMappings = resultMap.getIdResultMappings();
        if (resultMappings.isEmpty()) {
            resultMappings = resultMap.getPropertyResultMappings();
        }

        return resultMappings;
    }

    private void createRowKeyForMappedProperties(ResultMap resultMap, ResultSetWrapper rsw, CacheKey cacheKey, List<ResultMapping> resultMappings, String columnPrefix) throws SQLException {
        Iterator var6 = resultMappings.iterator();

        while(true) {
            String column;
            Object value;
            do {
                TypeHandler th;
                List mappedColumnNames;
                do {
                    do {
                        ResultMapping resultMapping;
                        do {
                            if (!var6.hasNext()) {
                                return;
                            }

                            resultMapping = (ResultMapping)var6.next();
                        } while(!resultMapping.isSimple());

                        column = this.prependPrefix(resultMapping.getColumn(), columnPrefix);
                        th = resultMapping.getTypeHandler();
                        mappedColumnNames = rsw.getMappedColumnNames(resultMap, columnPrefix);
                    } while(column == null);
                } while(!mappedColumnNames.contains(column.toUpperCase(Locale.ENGLISH)));

                value = th.getResult(rsw.getResultSet(), column);
            } while(value == null && !this.configuration.isReturnInstanceForEmptyRow());

            cacheKey.update(column);
            cacheKey.update(value);
        }
    }

    private void createRowKeyForUnmappedProperties(ResultMap resultMap, ResultSetWrapper rsw, CacheKey cacheKey, String columnPrefix) throws SQLException {
        MetaClass metaType = MetaClass.forClass(resultMap.getType(), this.reflectorFactory);
        List<String> unmappedColumnNames = rsw.getUnmappedColumnNames(resultMap, columnPrefix);
        Iterator var7 = unmappedColumnNames.iterator();

        while(true) {
            String column;
            String property;
            while(true) {
                if (!var7.hasNext()) {
                    return;
                }

                column = (String)var7.next();
                property = column;
                if (columnPrefix == null || columnPrefix.isEmpty()) {
                    break;
                }

                if (column.toUpperCase(Locale.ENGLISH).startsWith(columnPrefix)) {
                    property = column.substring(columnPrefix.length());
                    break;
                }
            }

            if (metaType.findProperty(property, this.configuration.isMapUnderscoreToCamelCase()) != null) {
                String value = rsw.getResultSet().getString(column);
                if (value != null) {
                    cacheKey.update(column);
                    cacheKey.update(value);
                }
            }
        }
    }

    private void createRowKeyForMap(ResultSetWrapper rsw, CacheKey cacheKey) throws SQLException {
        List<String> columnNames = rsw.getColumnNames();
        Iterator var4 = columnNames.iterator();

        while(var4.hasNext()) {
            String columnName = (String)var4.next();
            String value = rsw.getResultSet().getString(columnName);
            if (value != null) {
                cacheKey.update(columnName);
                cacheKey.update(value);
            }
        }

    }

    private void linkObjects(MetaObject metaObject, ResultMapping resultMapping, Object rowValue) {
        Object collectionProperty = this.instantiateCollectionPropertyIfAppropriate(resultMapping, metaObject);
        if (collectionProperty != null) {
            MetaObject targetMetaObject = this.configuration.newMetaObject(collectionProperty);
            targetMetaObject.add(rowValue);
        } else {
            metaObject.setValue(resultMapping.getProperty(), rowValue);
        }

    }

    private Object instantiateCollectionPropertyIfAppropriate(ResultMapping resultMapping, MetaObject metaObject) {
        String propertyName = resultMapping.getProperty();
        Object propertyValue = metaObject.getValue(propertyName);
        if (propertyValue == null) {
            Class<?> type = resultMapping.getJavaType();
            if (type == null) {
                type = metaObject.getSetterType(propertyName);
            }

            try {
                if (this.objectFactory.isCollection(type)) {
                    propertyValue = this.objectFactory.create(type);
                    metaObject.setValue(propertyName, propertyValue);
                    return propertyValue;
                }
            } catch (Exception var7) {
                throw new ExecutorException("Error instantiating collection property for result '" + resultMapping.getProperty() + "'.  Cause: " + var7, var7);
            }
        } else if (this.objectFactory.isCollection(propertyValue.getClass())) {
            return propertyValue;
        }

        return null;
    }

    private boolean hasTypeHandlerForResultObject(ResultSetWrapper rsw, Class<?> resultType) {
        return rsw.getColumnNames().size() == 1 ? this.typeHandlerRegistry.hasTypeHandler(resultType, rsw.getJdbcType((String)rsw.getColumnNames().get(0))) : this.typeHandlerRegistry.hasTypeHandler(resultType);
    }

    private static class UnMappedColumnAutoMapping {
        private final String column;
        private final String property;
        private final TypeHandler<?> typeHandler;
        private final boolean primitive;

        public UnMappedColumnAutoMapping(String column, String property, TypeHandler<?> typeHandler, boolean primitive) {
            this.column = column;
            this.property = property;
            this.typeHandler = typeHandler;
            this.primitive = primitive;
        }
    }

    private static class PendingRelation {
        public MetaObject metaObject;
        public ResultMapping propertyMapping;

        private PendingRelation() {
        }
    }
}
